#include "s3c2440_soc.h"
#include "nand.h"
#include "uart.h"
#include "my_printf.h"
#include "string_utils.h"

/*
初始化--识别--读--写--擦除
*/
/*
	CLE
	ALE
	nFRE
	nFWE
	nFCE
	RnB
------------------------------------------------------------------------------------------------------------
TACLS：从发出CLE/ALE(命令/地址)后，等待TACLS时间，发出nWE(读写)信号，从下图可以得出，TACLS = Tcls-Twp（等价于Tals-Twp）= 12ns-5ns=7ns
TWRPH0:表示这个nWE(读写)脉冲宽度，查表得为Twp，就是12ns
TWRPH1:表示在nWE(读写)脉冲结束后，经过TWRPH1时间，CLE/ALE(命令/地址)也结束，查看下图，TWRPH1就等于tCLH/tALH，查表得为5ns
-----------------------为以下做修正-----------------------------------------------------------------------

NFCONF 0x4E000000 NAND Flash 配置寄存器 -- 用于初始化
	TACLS [13:12] CLE 和ALE 持续值设置（0 至3） Duration = HCLK × TACLS--命令/地址锁存时间--2
		CLE:tWP + tCLH = 12 + 5, ALE:tALS+tALH= 12 +5,都取20ns, 那么TACLS=2
	TWRPH0 [10:8] TWRPH0 持续值设置（0~7） Duration = HCLK × ( TWRPH0 + 1 )--读写脉冲时间--1
		TWRPH0 = tWP = 12ns，取20ns，那么10ns*（1+1）=20ns，TWRPH0=1
	TWRPH1 [6:4] TWRPH1 持续值设置（0~7） Duration = HCLK × ( TWRPH1 + 1 )--数据保持时间--1
		Data:tDS+tDH=12+5, 取20ns，那么TWRPH1 = 1;

	BusWidth（R/W）[0]	0=8 位总线；1=16 位总线 ---- 1
		此位由在复位和从睡眠模式中唤醒时的GPG15 引脚状态所决定。复位后，GPG15 可以用于通用I/O 口或外部中断。此位可以被软件改变。

NFCONT 0x4E000004 R/W NAND Flash 控制寄存器
	Reg_nCE [1]  NAND Flash 存储器nFCE 信号控制 0：强制 nFCE 为低（使能片选） 1：强制 nFCE 为高（禁止片选）
	MODE [0]	NAND Flash 控制器运行模式 0：NAND Flash 控制器禁止（不工作） 1：NAND Flash 控制器使能 ---- 1

NFCMMD 0x4E000008 R/W NAND Flash 命令集寄存器

NFADDR 0x4E00000C R/W NAND Flash 地址集寄存器

NFDATA 0x4E000010 R/W NAND Flash 数据寄存器


*/
void nand_init(void)

{
	NFCONF &= ~((3<<12) | (3<<8) | (3<<4));	//初始化控制器
	//NFCONF |= (2<<12) | (1<<8) | (1<<4);	//错误
	NFCONF = (0<<12) | (4<<8) | (2<<4);

	/*使能NAND FLASH控制器,初始化ECC，禁止片选*/
	//NFCONT |= (1<<0);	//1：NAND Flash 控制器使能------配置错误20190403
	NFCONT = (1<<6)|(1<<5)|(1<<4)|(0<<1)|(1<<0);
}

void nand_select(void)
{
	NFCONT &= ~(1<<1);
}

void nand_deselect(void)
{
	NFCONT |= (1<<1);
}

void nand_cmd(unsigned char cmd)
{
	NFCMD = cmd;
}

void nand_addr(unsigned char addr)
{
	NFADDR = addr;
}

unsigned char nand_rdata(void)
{
	return NFDATA;
}

void nand_wdata(unsigned char val)
{
	NFDATA = val;
}


/*NFSTAT 0x4E000020 R/W NAND Flash 运行状态寄存器
	RnB（只读） [0] RnB 输入引脚的状态 0：NAND Flash 存储器忙 1：NAND Flash 存储器运行就绪*/
void wait_ready(void)
{
	while(!(NFSTAT & 1));		//NFSTAT & 1  表示取值，逻辑值
}

void nand_read_id(void)

{
	unsigned char buffer[6] = {0};
	nand_select();
	nand_cmd(0x90);
	nand_addr(0x00);

	buffer[0] = nand_rdata();
	buffer[1] = nand_rdata();
	buffer[2] = nand_rdata();
	buffer[3] = nand_rdata();
	buffer[4] = nand_rdata();

	nand_deselect();

	printf("Maker Code 	= 0x%02x \r\n", buffer[0]);
	printf("Device Code	= 0x%02x \r\n", buffer[1]);
	printf("3rd cyc. 	= 0x%02x \r\n", buffer[2]);
	printf("4th cyc. 	= 0x%02x \r\n", buffer[3]);

	printf("page size 	= %d KBytes \r\n", (1<<(buffer[3] & 0x3)));		//最低2位，&0x3就是取值，然后基数是1=2^0, 2=2^1,那么就是1*2^n就是1左移2^n,左移就是乘法
	printf("block size 	= %d KBytes \r\n", (64<<((buffer[3]>>4) & 0x3)));	//同上，基数是64

	printf("5th cyc. 	= 0x%02x \r\n", buffer[4]);

	printf("Plane Size 	= %d Mb \r\n", (64<<((buffer[4]>>4)&0x7)));
	printf("Plane Size 	= %d MBytes \r\n", ((64/8)<<((buffer[4]>>4)&0x7)));
}

void nand_read(unsigned int addr, unsigned char *buffer, unsigned int len)
{
	int page = addr / 2048; //一个page有2048个字节
	int col  = addr % 2048;	//取余数就是列	same as:col = addr &(2048-1) = addr & (0x7ff) //0d2048 = 0x800
	int i = 0;

	//选中
	nand_select();

	while(1)
	{
		//发命令	 00h
		nand_cmd(0x00);

		//发地址
		//发Col. Add.1,2
		nand_addr(col & 0xff);
		nand_addr((col>>8) & 0xff);

		//发Row   Add.1,2,3
		nand_addr(page & 0xff);
		nand_addr((page>>8) & 0xff);
		nand_addr((page>>16) & 0xff);

		//发命令	 30h
		nand_cmd(0x30);

		//---等待就绪---未看时序图，漏了！！！
		wait_ready();

		//读数据--每次最多独处1个page的数据.
		//每次从col的位置读（注意col不一定都在起始位置，但一定小于2048）， buffer偏移，直到读到len那么长的时候，就要停止了
		for(; (col<2048) && (i<len); col++)
		{
			buffer[i++] = nand_rdata();
		}

		if(i == len)
		{
			break;
		}

		//每读完1个page，列col从0开始，加一个page
		col = 0;
		page++;
	}

	//取消选中
	nand_deselect();
}

void nand_erase(unsigned int addr, unsigned int len)  //从addr开始，擦除len长度
{//Block Erase Operation, 擦除是按block执行，1block = 64page = 64*2KBytes =128KBytes

	int page = 4096/2048;	//addr/2048;

	//int block = 256;	//len/(64*2048);
	len = 1024 * 128 *100;
	addr = 1024 * 128 *100;
	//select
	nand_select();

	while(1)
	{
		page = addr/2048;
		//cmd
		nand_cmd(0x60);

		//addr--3 row addr
		nand_addr(page&0xff);
		nand_addr((page>>8)&0xff);
		nand_addr((page>>16)&0xff);

		//cmd
		nand_cmd(0xd0);

		//ready/busy!!!
		wait_ready();

		//cmd
		nand_cmd(0x70);

		printf("addr = %d , len = %d \r\n", addr, len);

		if(nand_rdata() == 0)
		{
			printf("Successful Erase!!!\r\n");
		}
		len -= (64*2048); //每擦除1个block后，减去1个block大小的Bytes

		if(len <= 0)
			break;
		addr += (64*2048);
	}
	//deselect
	nand_deselect();
}

void nand_write(unsigned int addr, unsigned char * buffer, unsigned int len)
{
	//select
	nand_select();
	//deselect
	nand_deselect();
}

int  nand_block_erase(unsigned int addr, unsigned int len)
{//block = 64 page = 64 * 2k
	int page = addr / 2048;
	//int i = 0;

	if(addr&0x1ffff)
	{
		printf("addr is not block align\r\n");
		return -1;
	}

	if(len&0x1ffff)
	{
		printf("len is not block align\r\n");
		return -1;
	}
	//少了选中与取消选中！！！nand_select();
	while(1)
	{
		//page = addr / 2048;  //对应下面的page，换成addr += (64*2048) ////(addr + 64*2048)/2048 = addr/2048 + 64 = page + 64
		//cmd
		nand_cmd(0x60);

		//addr 3-raw addr
		nand_addr(page&0xff);
		nand_addr((page>>8)&0xff);
		nand_addr((page>>16)&0xff);	
		
		//cmd
		nand_cmd(0xD0);
		
		//wait
		wait_ready();
		
#if 0	//擦除是自动完成，只要等待no busy即可，然后做适当的偏移
		//data--64 page = 64 * 2k write 0xff   擦除必须是页对齐，就是说col每次都是从0开始，第一次也是
		for(; (i<2048) && (page < 64); i++)
		{
			nand_wdata(0xff);
		}
#endif
		len -= (64*2048);	//偏移block
		if(len == 0)
			break;
		page += 64;
	}
	return 0;
}

void nand_page_write(unsigned int addr, unsigned char * buffer, unsigned int len)
{	
	int page = addr / 2048;
	int col  = addr % 2048; //same as:col = addr &(2048-1) = addr & (0x7ff) //0d2048 == 0x800
	int i = 0;

	//少了选中与取消选中！！！nand_select();
	while(1)
	{
		//cmd
		nand_cmd(0x80);

		//addr
		//2--col addr
		nand_addr(col&0xff);
		nand_addr((col>>8)&0xff);

		//3--raw/page addr
		nand_addr(page&0xff);
		nand_addr((page>>8)&0xff);
		nand_addr((page>>16)&0xff);

		for(; (col<2048) && (i<len); col++)
		{
			nand_wdata(buffer[i++]);	//nand_wdata()= buffer[i++];	//错误写法
		}

		//cmd
		nand_cmd(0x10);

		//wait
		wait_ready();
		
		if(i == len)
			break;

		col = 0;
		page++; 	
	}
}

void do_nand_read()
{

}

void do_nand_erase()
{

}

void do_nand_write()
{


}

